<?php
/**
 * Created by PhpStorm.
 * User: longli
 * Date: 2021/09/08
 * Time: 17:43
 * @link http://www.lmterp.cn
 */

namespace app\common\service\orders;

use app\common\library\Tools;
use app\common\model\Channel;
use app\common\model\ChannelAttr;
use app\common\model\OrdersRules;
use app\common\model\OrdersRulesGroup;
use app\common\model\Orders;
use app\common\model\Product;
use app\common\model\ProductStore;
use app\common\service\BaseService;

/**
 * 解析自定义规则
 * Class ParseRuleService
 * @package app\common\service\system
 */
class ParseRuleService extends BaseService
{

    /**
     * 规则类型
     * @var string
     */
    private $refType;

    /**
     * 订单
     * @var Orders
     */
    private $order;


    public function __construct($refType, $order)
    {
        $this->refType = $refType;
        $this->order = $order;
    }

    /**
     * 获取 id 集合
     * @return array
     * @date 2021/09/08
     * @author longli
     */
    public function getIds()
    {
        return $this->getGroupRules();
    }

    /**
     * 获取配置规则
     * @param int $count 每次处理的条数，默认每次只处理 50条
     * @return array
     * @date 2021/08/26
     * @author longli
     */
    private function getGroupRules($count = 50)
    {
        $result = [
            'include' => [],
            'exclude' => [],
        ];
        OrdersRulesGroup::with(["rules"])
        ->where([
            ["status", "eq", OrdersRulesGroup::IS_YES],
            ["ref_type", "eq", $this->refType],
        ])
        ->chunk($count, function($groups) use(& $result)
        {
            foreach($groups as $group)
            {
                $result = $this->parseGroupRules($group);
                if(!empty($result['include']) || !empty($result['exclude'])) return false;
            }
        }, "sort", "desc");
        return $result;
    }

    /**
     * 解析规则组
     * @param OrdersRulesGroup $group 规则分组
     * @return array 渠道id
     * @date 2021/08/26
     * @author longli
     */
    private function parseGroupRules(OrdersRulesGroup $group)
    {
        $include = $exclude = [];
        $matchType = null;
        foreach($group->rules as $k => $rule)
        {
            if(empty($rule->list_id)) continue;
            if($this->parseRule($rule))
            {
                $cList = explode(',', $rule->list_id);
                // 排除
                if(!$rule->is_include)
                {
                    $exclude = array_merge($exclude, $cList);
                    continue;
                }
                // 包含
                if(empty($matchType) || in_array($matchType, [OrdersRules::MATCH_TYPE_END, OrdersRules::MATCH_TYPE_OR]))
                {
                    $include = array_merge($include, $cList);
                }
                else if($matchType == OrdersRules::MATCH_TYPE_AND)
                {
                    $include = array_intersect($include, $cList);
                    if(empty($include)) break;
                }
            }
            else if($rule->is_include && $matchType == OrdersRules::MATCH_TYPE_AND)
            {
                return [
                    'include' => [],
                    'exclude' => array_unique($exclude)
                ];
            }
            $matchType = $rule->getData('match_type');
            // 如果没有下一条则不需要解析
            if(!$rule->next_node || $matchType == OrdersRules::MATCH_TYPE_END) break;
        }
        return [
            'include' => array_unique($include),
            'exclude' => array_unique($exclude)
        ];
    }

    /**
     * 解析单条规则
     * @param OrdersRules $rule
     * @return bool
     * @date 2021/08/26
     * @author longli
     */
    private function parseRule(OrdersRules $rule)
    {
        switch($rule->getData('rule_type'))
        {
            case OrdersRules::RULE_TYPE_PLATFORM: // 平台
                return $this->parseCondition($rule, $this->order->account->platform->code);
            case OrdersRules::RULE_TYPE_ACCOUNT: // 账号
                return $this->parseCondition($rule, $this->order->account->username);
            case OrdersRules::RULE_TYPE_SPU: // SPU
                $spu = Product::whereIn("product_id", function($query)
                {
                    $query->table(ProductStore::getTable())
                        ->whereIn("sku", $this->order->getSkus())
                        ->field("product_id");
                })->column("spu");
                return $this->parseCondition($rule, $spu);
            case OrdersRules::RULE_TYPE_SKU: // SKU
                return $this->parseCondition($rule, $this->order->getSkus());
            case OrdersRules::RULE_TYPE_SKU_PREFIX: // SKU 前缀
            case OrdersRules::RULE_TYPE_SKU_SUFFIX: // SKU 后缀
                return $this->parsePreSuf($rule, $this->order->getSkus());
            case OrdersRules::RULE_TYPE_WEIGHT: // 重量
                return $this->parseCondition($rule, $this->order->getWeight());
            case OrdersRules::RULE_TYPE_MONEY: // 价格
                return $this->parseCondition($rule, $this->order->total_price);
            case OrdersRules::RULE_TYPE_COUNTRY: // 国家
                return $this->parseCondition($rule, $this->order->buyer_country_code);
            case OrdersRules::RULE_TYPE_LAST_SEND: // 最迟发货
                $lastDay = Tools::diffDays(time(), $this->order->latest_delivery_time);
                //  是否过期
                if($this->order->latest_delivery_time < Tools::now()) $lastDay = -$lastDay;
                return $this->parseCondition($rule, $lastDay);
            case OrdersRules::RULE_TYPE_ATTR: // 物流属性
                $attr = ChannelAttr::getCattrBySku($this->order->getSkus());
                return !empty($attr) ? $this->parseCondition($rule, $attr) : true;
        }
        return false;
    }

    /**
     * 解析单条规则处理前后缀
     * @param OrdersRules $rule
     * @param string|array $value 处理数据
     * @return bool
     * @date 2021/08/26
     * @author longli
     */
    private function parsePreSuf(OrdersRules $rule, $value)
    {
        if(empty($rule->rule_value)) return false;
        $condition = $rule->getData("condition");
        $ruleValue = explode(',', $rule->rule_value);
        if(!is_array($value)) $value = [$value];
        $fun = $rule->getData("rule_type") == OrdersRules::RULE_TYPE_SKU_PREFIX ? "startWith" : "endWith";
        $isNot = $match = false;
        foreach($value as $k => $v)
        {
            if($k > 0 && !$match && !$isNot) break;
            $match = false;
            foreach($ruleValue as $rv)
            {
                if(call_user_func([Tools::class, $fun], $v, $rv))
                {
                    $match = true;
                    break;
                }
            }
            if(in_array($condition, [OrdersRules::CONDITION_NOT_IN, OrdersRules::CONDITION_NEQ]))
            {
                $isNot = true;
                if($match) return false;
            }
        }
        switch($condition)
        {
            case OrdersRules::CONDITION_IN:
            case OrdersRules::CONDITION_EQ:
                return $match;
            case OrdersRules::CONDITION_NOT_IN:
            case OrdersRules::CONDITION_NEQ:
                return !$match;
        }
    }

    /**
     * 解析单条规则判断条件
     * @param OrdersRules $rule
     * @param string|array $value 处理数据
     * @return bool
     * @date 2021/08/26
     * @author longli
     */
    private function parseCondition(OrdersRules $rule, $value)
    {
        if(empty($rule->rule_value)) return false;
        $ruleValue = explode(',', $rule->rule_value);
        if(!is_array($value)) $value = [$value];
        switch($rule->getData("condition"))
        {
            case OrdersRules::CONDITION_IN:
            case OrdersRules::CONDITION_EQ:
                if(Tools::containArray($ruleValue, $value)) return true;
                break;
            case OrdersRules::CONDITION_NOT_IN:
            case OrdersRules::CONDITION_NEQ:
                if(!array_intersect($ruleValue, $value)) return true;
                break;
            case OrdersRules::CONDITION_GT:
                if($value[0] > $rule->rule_value) return true;
                break;
            case OrdersRules::CONDITION_EGT:
                if($value[0] >= $rule->rule_value) return true;
                break;
            case OrdersRules::CONDITION_LT:
                if($value[0] < $rule->rule_value) return true;
                break;
            case OrdersRules::CONDITION_ELT:
                if($value[0] <= $rule->rule_value) return true;
                break;
            case OrdersRules::CONDITION_BETWEEN:
                $ruleValue = explode(',', str_replace('-', ',', $rule->rule_value));
                if($ruleValue[0] <= $value[0] && $ruleValue[1] >= $value[0]) return true;
                break;
        }
        return false;
    }
}